﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using LabColorPicker.Properties;
using Zive.Color;

namespace LabColorPicker {

    public partial class ColorPickerWindow: Window {

        //===============
        //   Interface
        //===============

        private ColorData _colorData = new ColorData( );
        public ColorData ColorData {
            get { return _colorData; }
            set { _colorData = value; }
        }

        //
        // Constructors
        //

        public ColorPickerWindow( ) {
            InitializeComponent( );

            _currentColorBrush = (SolidColorBrush) Resources[ "CurrentColorBrush" ];

            _twoChannelBitmapMaker = _labTwoChannelBitmapMaker;
            _oneChannelBitmapMaker = _labOneChannelBitmapMaker;

            // Initialize watchdog timer
            _watchdogTimer = new DispatcherTimer {
                Interval = new TimeSpan( 0, 0, 1 ),
            };
            _watchdogTimer.Tick += WatchdogTimer_Tick;

            _editorMap = new Dictionary<string, ThreeChannelValueEditor> {
                { "Rgb", RgbEditor },
                { "Lab", LabEditor },
                { "Hsl", HslEditor },
            };

            // Try to restore color
            _TryRestoreLastColor( );

            // Try to restore selected radio button
            _TryRestoreLastRadioButton( );
        }

        //====================
        //   Implementation
        //====================

        private static readonly char[ ] _commaDelimiter = { ',' };

        private readonly RgbOneChannelBitmapMaker _rgbOneChannelBitmapMaker = new RgbOneChannelBitmapMaker( 32, 256 );
        private readonly RgbTwoChannelBitmapMaker _rgbTwoChannelBitmapMaker = new RgbTwoChannelBitmapMaker( 256, 256 );

        private readonly LabOneChannelBitmapMaker _labOneChannelBitmapMaker = new LabOneChannelBitmapMaker( 32, 256 );
        private readonly LabTwoChannelBitmapMaker _labTwoChannelBitmapMaker = new LabTwoChannelBitmapMaker( 256, 256 );

        private readonly HslOneChannelBitmapMaker _hslOneChannelBitmapMaker = new HslOneChannelBitmapMaker( 32, 256 );
        private readonly HslTwoChannelBitmapMaker _hslTwoChannelBitmapMaker = new HslTwoChannelBitmapMaker( 256, 256 );

        private readonly Dictionary<string, ThreeChannelValueEditor> _editorMap;

        private readonly Settings _settings = Settings.Default;

        private readonly SolidColorBrush _currentColorBrush;

        private readonly DispatcherTimer _watchdogTimer;

        private OneChannelBitmapMaker _oneChannelBitmapMaker;
        private TwoChannelBitmapMaker _twoChannelBitmapMaker;

        private ColorData _originalColorData;

        private bool _changing;

        //
        // Methods
        //

        private void _TryRestoreLastColor( ) {
            _colorData = new ColorData( );
            _colorData.PropertyChanged += ColorData_PropertyChanged;
            DataContext = _colorData;

            try {
                _colorData.Set( _settings.LastColor );
            }
            catch ( Exception e ) {
                Debug.Print( "_TryRestoreLastColor: caught exception while trying to restore saved color value:\n" + e );
                if ( !_colorData.IsSet ) {
                    //_colorData.Rgb = new RgbColor( 40, 116, 244 );
                    _colorData.Lab = new LabColor( Illuminants.D50, 70.71d, 0d, 0d );
                }
            }
            ( (SolidColorBrush) Resources[ "OriginalColorBrush" ] ).Color = _colorData.Rgb;
        }

        private void _SelectRadioButton( string editor, int index ) {
            foreach ( var name in _editorMap.Keys ) {
                _editorMap[ name ].SelectedIndex = editor.Equals( name, StringComparison.InvariantCultureIgnoreCase ) ? index : 0;
            }
        }

        private void _TryRestoreLastRadioButton( ) {
            try {
                var parts = _settings.LastRadioButton.Split( _commaDelimiter, StringSplitOptions.RemoveEmptyEntries );
                Debug.Print( "_TryRestoreLastRadioButton: LastRadioButton='{0}' parts[0]='{1}' parts[1]='{2}'", _settings.LastRadioButton, parts[ 0 ], parts[ 1 ] );
                _editorMap[ parts[ 0 ] ].SelectedIndex = int.Parse( parts[ 1 ], NumberStyles.Integer );
                _SelectRadioButton( parts[ 0 ], int.Parse( parts[ 1 ], NumberStyles.Integer ) );
            }
            catch ( Exception e ) {
                Debug.Print( "ColorPickerWindow.`ctor: caught exception while trying to restore saved radio button:\n" + e );
                _SelectRadioButton( "Lab", 1 );
            }
        }

        private void _TryRestoreLastWindowPosition( ) {
            if ( string.IsNullOrWhiteSpace( _settings.LastWindowPosition ) ) {
                return;
            }

            Point topLeft;
            try {
                var bits = _settings.LastWindowPosition.Split( _commaDelimiter, StringSplitOptions.RemoveEmptyEntries );
                if ( bits.Length != 2 ) {
                    return;
                }
                topLeft = new Point( int.Parse( bits[ 0 ], NumberStyles.Integer ), int.Parse( bits[ 1 ], NumberStyles.Integer ) );
            }
            catch ( FormatException ) {
                return;
            }
            catch ( Exception e ) {
                Debug.Print( "Window_Loaded: caught exception while trying to restore saved window position:\n" + e );
                return;
            }

            Left = topLeft.X;
            Top = topLeft.Y;
        }

        private void _UpdateSliceBitmaps( ) {
            TwoDimensionalSlice.Source = _twoChannelBitmapMaker.Bitmap;
            OneDimensionalSlice.Source = _oneChannelBitmapMaker.Bitmap;
        }

        //
        // Event handlers
        //

        // Window

        private void Window_Loaded( object sender, RoutedEventArgs ev ) {
            _TryRestoreLastWindowPosition( );
        }

        private void Window_LocationChanged( object sender, EventArgs ev ) {
            if ( _watchdogTimer.IsEnabled ) {
                _watchdogTimer.Stop( );
            }
            _watchdogTimer.Start( );
        }

        // WatchdogTimer

        private void WatchdogTimer_Tick( object sender, EventArgs ev ) {
            _watchdogTimer.Stop( );

            _settings.LastWindowPosition = string.Format( "{0},{1}", Left, Top );
            _settings.Save( );
        }

        // TwoDimensionalSlice

        private void TwoDimensionalSlice_ChangeStarting( object sender, EventArgs ev ) {
            _changing = true;
        }

        private void TwoDimensionalSlice_ValueChanged( object sender, EventArgs ev ) {
            if ( _twoChannelBitmapMaker is LabTwoChannelBitmapMaker ) {
                _colorData.Lab[ _labTwoChannelBitmapMaker.HorizontalChannel ] = TwoDimensionalSlice.HorizontalValue;
                _colorData.Lab[ _labTwoChannelBitmapMaker.VerticalChannel ] = TwoDimensionalSlice.VerticalValue;
            } else if ( _twoChannelBitmapMaker is RgbTwoChannelBitmapMaker ) {
                _colorData.Rgb[ _rgbTwoChannelBitmapMaker.HorizontalChannel ] = TwoDimensionalSlice.HorizontalValue;
                _colorData.Rgb[ _rgbTwoChannelBitmapMaker.VerticalChannel ] = TwoDimensionalSlice.VerticalValue;
            } else if ( _twoChannelBitmapMaker is HslTwoChannelBitmapMaker ) {
                _colorData.Hsl[ _hslTwoChannelBitmapMaker.HorizontalChannel ] = TwoDimensionalSlice.HorizontalValue;
                _colorData.Hsl[ _hslTwoChannelBitmapMaker.VerticalChannel ] = TwoDimensionalSlice.VerticalValue;
            }
        }

        private void TwoDimensionalSlice_ChangeFinished( object sender, EventArgs ev ) {
            _changing = false;
            _settings.LastColor = new ColorData( _colorData );
            _settings.Save( );
        }

        // OneDimensionalSlice

        private void OneDimensionalSlice_ChangeStarting( object sender, EventArgs ev ) {
            _changing = true;
        }

        private void OneDimensionalSlice_ValueChanged( object sender, EventArgs ev ) {
            if ( _oneChannelBitmapMaker is LabOneChannelBitmapMaker ) {
                _colorData.Lab[ _labOneChannelBitmapMaker.Channel ] = OneDimensionalSlice.Value;
            } else if ( _oneChannelBitmapMaker is RgbOneChannelBitmapMaker ) {
                _colorData.Rgb[ _rgbOneChannelBitmapMaker.Channel ] = OneDimensionalSlice.Value;
            } else if ( _oneChannelBitmapMaker is HslOneChannelBitmapMaker ) {
                _colorData.Hsl[ _hslOneChannelBitmapMaker.Channel ] = OneDimensionalSlice.Value;
            }
        }

        private void OneDimensionalSlice_ChangeFinished( object sender, EventArgs ev ) {
            _changing = false;
            _settings.LastColor = new ColorData( _colorData );
            _settings.Save( );
        }

        // RGB editor

        private void RgbEditor_RChanged( object sender, EventArgs ev ) {

        }

        private void RgbEditor_GChanged( object sender, EventArgs ev ) {

        }

        private void RgbEditor_BChanged( object sender, EventArgs ev ) {

        }

        private void RgbEditor_RClicked( object sender, EventArgs ev ) {
            _settings.LastRadioButton = "Rgb,1";
            _settings.Save( );

            _rgbTwoChannelBitmapMaker.HorizontalChannel = RgbChannels.B;
            _rgbTwoChannelBitmapMaker.VerticalChannel = RgbChannels.G;
            _rgbOneChannelBitmapMaker.Channel = RgbChannels.R;

            HorizontalAxisLabel.Content = "B";
            VerticalAxisLabel.Content = "G";
            SingleAxisLabel.Content = "R";

            _twoChannelBitmapMaker = _rgbTwoChannelBitmapMaker;
            _oneChannelBitmapMaker = _rgbOneChannelBitmapMaker;
            _UpdateSliceBitmaps( );

            TwoDimensionalSlice.SetMinimaMaximaAndValues(
                0d, 255d, _colorData.Rgb.B,
                0d, 255d, _colorData.Rgb.G
            );
            OneDimensionalSlice.SetMinimumMaximumAndValue(
                0d, 255d, _colorData.Rgb.R
            );
        }

        private void RgbEditor_GClicked( object sender, EventArgs ev ) {
            _settings.LastRadioButton = "Rgb,2";
            _settings.Save( );

            _rgbTwoChannelBitmapMaker.HorizontalChannel = RgbChannels.B;
            _rgbTwoChannelBitmapMaker.VerticalChannel = RgbChannels.R;
            _rgbOneChannelBitmapMaker.Channel = RgbChannels.G;

            HorizontalAxisLabel.Content = "B";
            VerticalAxisLabel.Content = "R";
            SingleAxisLabel.Content = "G";

            _twoChannelBitmapMaker = _rgbTwoChannelBitmapMaker;
            _oneChannelBitmapMaker = _rgbOneChannelBitmapMaker;
            _UpdateSliceBitmaps( );

            TwoDimensionalSlice.SetMinimaMaximaAndValues(
                0d, 255d, _colorData.Rgb.B,
                0d, 255d, _colorData.Rgb.R
            );
            OneDimensionalSlice.SetMinimumMaximumAndValue(
                0d, 255d, _colorData.Rgb.G
            );
        }

        private void RgbEditor_BClicked( object sender, EventArgs ev ) {
            _settings.LastRadioButton = "Rgb,3";
            _settings.Save( );

            _rgbTwoChannelBitmapMaker.HorizontalChannel = RgbChannels.R;
            _rgbTwoChannelBitmapMaker.VerticalChannel = RgbChannels.G;
            _rgbOneChannelBitmapMaker.Channel = RgbChannels.B;

            HorizontalAxisLabel.Content = "R";
            VerticalAxisLabel.Content = "G";
            SingleAxisLabel.Content = "B";

            _twoChannelBitmapMaker = _rgbTwoChannelBitmapMaker;
            _oneChannelBitmapMaker = _rgbOneChannelBitmapMaker;
            _UpdateSliceBitmaps( );

            TwoDimensionalSlice.SetMinimaMaximaAndValues(
                0d, 255d, _colorData.Rgb.R,
                0d, 255d, _colorData.Rgb.G
            );
            OneDimensionalSlice.SetMinimumMaximumAndValue(
                0d, 255d, _colorData.Rgb.B
            );
        }

        // L*a*b* editor

        private void LabEditor_LChanged( object sender, EventArgs ev ) {

        }

        private void LabEditor_AChanged( object sender, EventArgs ev ) {

        }

        private void LabEditor_BChanged( object sender, EventArgs ev ) {

        }

        private void LabEditor_LClicked( object sender, EventArgs ev ) {
            _settings.LastRadioButton = "Lab,1";
            _settings.Save( );

            _labTwoChannelBitmapMaker.HorizontalChannel = LabChannels.A;
            _labTwoChannelBitmapMaker.VerticalChannel = LabChannels.B;
            _labOneChannelBitmapMaker.Channel = LabChannels.L;

            HorizontalAxisLabel.Content = "a*";
            VerticalAxisLabel.Content = "b*";
            SingleAxisLabel.Content = "L*";

            _twoChannelBitmapMaker = _labTwoChannelBitmapMaker;
            _oneChannelBitmapMaker = _labOneChannelBitmapMaker;
            _UpdateSliceBitmaps( );

            TwoDimensionalSlice.SetMinimaMaximaAndValues(
                -128d, 128d, _colorData.Lab.Astar,
                -128d, 128d, _colorData.Lab.Bstar
            );
            OneDimensionalSlice.SetMinimumMaximumAndValue(
                0d, 100d, _colorData.Lab.Lstar
            );
        }

        private void LabEditor_AClicked( object sender, EventArgs ev ) {
            _settings.LastRadioButton = "Lab,2";
            _settings.Save( );

            _labTwoChannelBitmapMaker.HorizontalChannel = LabChannels.B;
            _labTwoChannelBitmapMaker.VerticalChannel = LabChannels.L;
            _labOneChannelBitmapMaker.Channel = LabChannels.A;

            HorizontalAxisLabel.Content = "b*";
            VerticalAxisLabel.Content = "L*";
            SingleAxisLabel.Content = "a*";

            _twoChannelBitmapMaker = _labTwoChannelBitmapMaker;
            _oneChannelBitmapMaker = _labOneChannelBitmapMaker;
            _UpdateSliceBitmaps( );

            TwoDimensionalSlice.SetMinimaMaximaAndValues(
                -128d, 128d, _colorData.Lab.Bstar,
                0d, 100d, _colorData.Lab.Lstar
            );
            OneDimensionalSlice.SetMinimumMaximumAndValue(
                -128d, 128d, _colorData.Lab.Astar
            );
        }

        private void LabEditor_BClicked( object sender, EventArgs ev ) {
            _settings.LastRadioButton = "Lab,3";
            _settings.Save( );

            _labTwoChannelBitmapMaker.HorizontalChannel = LabChannels.A;
            _labTwoChannelBitmapMaker.VerticalChannel = LabChannels.L;
            _labOneChannelBitmapMaker.Channel = LabChannels.B;

            HorizontalAxisLabel.Content = "a*";
            VerticalAxisLabel.Content = "L*";
            SingleAxisLabel.Content = "b*";

            _twoChannelBitmapMaker = _labTwoChannelBitmapMaker;
            _oneChannelBitmapMaker = _labOneChannelBitmapMaker;
            _UpdateSliceBitmaps( );

            TwoDimensionalSlice.SetMinimaMaximaAndValues(
                -128d, 128d, _colorData.Lab.Astar,
                0d, 100d, _colorData.Lab.Lstar
            );
            OneDimensionalSlice.SetMinimumMaximumAndValue(
                -128d, 128d, _colorData.Lab.Bstar
            );
        }

        // HSL radio buttons

        private void HslEditor_HChanged( object sender, EventArgs ev ) {

        }

        private void HslEditor_SChanged( object sender, EventArgs ev ) {

        }

        private void HslEditor_LChanged( object sender, EventArgs ev ) {

        }

        private void HslEditor_HClicked( object sender, EventArgs ev ) {
            _settings.LastRadioButton = "Hsl,1";
            _settings.Save( );

            _hslTwoChannelBitmapMaker.HorizontalChannel = HslChannels.L;
            _hslTwoChannelBitmapMaker.VerticalChannel = HslChannels.S;
            _hslOneChannelBitmapMaker.Channel = HslChannels.H;

            HorizontalAxisLabel.Content = "L";
            VerticalAxisLabel.Content = "S";
            SingleAxisLabel.Content = "H";

            _twoChannelBitmapMaker = _hslTwoChannelBitmapMaker;
            _oneChannelBitmapMaker = _hslOneChannelBitmapMaker;
            _UpdateSliceBitmaps( );

            TwoDimensionalSlice.SetMinimaMaximaAndValues(
                0d, 100d, _colorData.Hsl.L,
                0d, 100d, _colorData.Hsl.S
            );
            OneDimensionalSlice.SetMinimumMaximumAndValue(
                0d, 360d, _colorData.Hsl.H
            );
        }

        private void HslEditor_SClicked( object sender, EventArgs ev ) {
            _settings.LastRadioButton = "Hsl,2";
            _settings.Save( );

            _hslTwoChannelBitmapMaker.HorizontalChannel = HslChannels.L;
            _hslTwoChannelBitmapMaker.VerticalChannel = HslChannels.H;
            _hslOneChannelBitmapMaker.Channel = HslChannels.S;

            HorizontalAxisLabel.Content = "L";
            VerticalAxisLabel.Content = "H";
            SingleAxisLabel.Content = "S";

            _twoChannelBitmapMaker = _hslTwoChannelBitmapMaker;
            _oneChannelBitmapMaker = _hslOneChannelBitmapMaker;
            _UpdateSliceBitmaps( );

            TwoDimensionalSlice.SetMinimaMaximaAndValues(
                0d, 100d, _colorData.Hsl.L,
                0d, 360d, _colorData.Hsl.H
            );
            OneDimensionalSlice.SetMinimumMaximumAndValue(
                0d, 100d, _colorData.Hsl.S
            );
        }

        private void HslEditor_LClicked( object sender, EventArgs ev ) {
            _settings.LastRadioButton = "Hsl,3";
            _settings.Save( );

            _hslTwoChannelBitmapMaker.HorizontalChannel = HslChannels.H;
            _hslTwoChannelBitmapMaker.VerticalChannel = HslChannels.S;
            _hslOneChannelBitmapMaker.Channel = HslChannels.L;

            HorizontalAxisLabel.Content = "H";
            VerticalAxisLabel.Content = "S";
            SingleAxisLabel.Content = "L";

            _twoChannelBitmapMaker = _hslTwoChannelBitmapMaker;
            _oneChannelBitmapMaker = _hslOneChannelBitmapMaker;
            _UpdateSliceBitmaps( );

            TwoDimensionalSlice.SetMinimaMaximaAndValues(
                0d, 360d, _colorData.Hsl.H,
                0d, 100d, _colorData.Hsl.S
            );
            OneDimensionalSlice.SetMinimumMaximumAndValue(
                0d, 100d, _colorData.Hsl.L
            );
        }

        // ColorData

        protected void ColorData_PropertyChanged( object sender, PropertyChangedEventArgs ev ) {
            if ( null == _originalColorData ) {
                _originalColorData = new ColorData( _colorData );
            }

            switch ( ev.PropertyName ) {
                case "Rgb":
                    _rgbOneChannelBitmapMaker.Color = _colorData.Rgb;
                    _rgbTwoChannelBitmapMaker.Color = _colorData.Rgb;
                    if ( _oneChannelBitmapMaker is RgbOneChannelBitmapMaker ) {
                        _UpdateSliceBitmaps( );
                    }
                    TextWebColor.Text = string.Format( "#{0,2:X2}{1,2:X2}{2,2:X2}", _colorData.Rgb.ByteR, _colorData.Rgb.ByteG, _colorData.Rgb.ByteB );
                    _currentColorBrush.Color = _colorData.Rgb;
                    break;

                case "Lab":
                    _labOneChannelBitmapMaker.Color = _colorData.Lab;
                    _labTwoChannelBitmapMaker.Color = _colorData.Lab;
                    if ( _oneChannelBitmapMaker is LabOneChannelBitmapMaker ) {
                        _UpdateSliceBitmaps( );
                    }
                    break;

                case "Hsl":
                    _hslOneChannelBitmapMaker.Color = _colorData.Hsl;
                    _hslTwoChannelBitmapMaker.Color = _colorData.Hsl;
                    if ( _oneChannelBitmapMaker is HslOneChannelBitmapMaker ) {
                        _UpdateSliceBitmaps( );
                    }
                    break;
            }
        }

        // OriginalColor

        private void OriginalColor_MouseLeftButtonDown( object sender, MouseButtonEventArgs ev ) {
            _colorData.Set( _originalColorData );
        }

        //
        // Commands
        //

        // InterruptChangeCommand

        private void InterruptChangeCommand_CanExecute( object sender, CanExecuteRoutedEventArgs ev ) {
            ev.CanExecute = _changing;
        }

        private void InterruptChangeCommand_Executed( object sender, ExecutedRoutedEventArgs ev ) {
            OneDimensionalSlice.InterruptChange( );
            TwoDimensionalSlice.InterruptChange( );
        }

    }

}
